#include "StdAfx.h"
#include "VSqlNode.h"

// VSqlNode ///////////////////////////////////////////////////////////////////

//
void VSqlNode::setRecord(VSqlSharedRecordPtr &r) const
{
	r->clear();
	foreach (const QSqlField field,fieldList())
		r->append(field.value());
}

// VSqlNode_insert ////////////////////////////////////////////////////////////

/******************************************************************************
create or alter procedure VidSqlNode_insert(
  idNodeParent bigint,
	refIdItem    bigint,
	refIdRec     bigint,
	status       integer
)
returns(
	idNode bigint,
 	pos    integer
)
as begin
  idNode = gen_id(VidGen,1);

	select max(pos)
	from   VidNode
	where  idNodeParent = :idNodeParent
	into   :pos;

	if (pos is null) then begin
		pos = 0;
	end else begin
		pos = pos + 1;
	end
  
	insert into VidNode( idNode, idNodeParent, pos, refIdItem, refIdRec, status)
							values (:idNode,:idNodeParent,:pos,:refIdItem,:refIdRec,:status);
	
	suspend;
end;
******************************************************************************/

//
VSqlNode_insert::VSqlNode_insert(const QSqlDatabase &db)
	:m_proc(db)
{
	VSqlNode sqlNode;
	QSqlField status = sqlNode.status();
	
	m_proc.prepare("VidNode_insert",VSqlFieldPtrList()
		<< sqlNode.idNodeParentPtr()
		<< sqlNode.refIdItemPtr()
		<< sqlNode.refIdRecPtr()
		<< &status
	);
}

//
void VSqlNode_insert::exec(VSqlNode &sqlNode)
{
	QSqlField status = sqlNode.status();
		status.setValue(ESqlDbStatus_actual);

	m_proc.exec(VSqlFieldPtrList()
		<< sqlNode.idNodeParentPtr()
		<< sqlNode.refIdItemPtr()
		<< sqlNode.refIdRecPtr()
		<< &status
	);
	
	m_proc.next(VSqlFieldPtrList()
		<< sqlNode.idNodePtr()
	);
}

// VSqlNode_select_byAnd ///////////////////////////////////////////////////

//
void VSqlNode_select_byAnd::prepare
(
	const VSqlFieldList &fieldsSelect,
	const VSqlFieldList &fieldsAnd
)
{
	VSqlNode sqlNode;
	QString sqlWhere = QString(sqlNode.status().name() + " = %1").arg(ESqlDbStatus_actual);

	foreach (const QSqlField fieldAnd,fieldsAnd)
		sqlWhere += " and " + fieldAnd.name() + " = :" + fieldAnd.name();

	m_query.prepare(
		sqlNode.tableName(),
		fieldsSelect,
		sqlWhere,
		VSqlFieldList() << sqlNode.pos()
	);
}

// VSqlNode_hasChildren ///////////////////////////////////////////////////////

//
VSqlNode_hasChildren::VSqlNode_hasChildren(const QSqlDatabase &db)
	:m_query(db)
{
	VSqlNode node;
	m_query.prepare
	(
		VSqlFieldList() << node.idNode(),
		VSqlFieldList() << node.idNodeParent()
	);
}

//
bool VSqlNode_hasChildren::exec(const QVariant &idNode)
{
	VSqlNode node;
	node.setIdNodeParent(idNode);
	m_query.exec(VSqlFieldList() << node.idNodeParent());
	
	return m_query.next(VSqlFieldPtrList() << node.idNodePtr());
}

// VSqlNodeMap ////////////////////////////////////////////////////////////////

//
VSqlNodeMap::VSqlNodeMap(const QSqlDatabase &db)
	:m_db(db) 
{
	//VSqlNodeChildren cc = children_byIdNode(684);
	//QList<VSqlNode> list = nodeList_byRefIdItem(703,684);
}

//
QList<VSqlSharedRecordPtr> VSqlNodeMap::recordPtrList_byIdNodeList(const QVariantList &idNodeList)
{
	QList<VSqlSharedRecordPtr> outList;
	VSqlNode node;
	VSqlFieldList fieldList = node.fieldList();
	VSqlFieldPtrList fieldPtrList = node.fieldPtrList();
	VSqlNode_select_byAnd selNode(m_db);

	VSqlNode_hasChildren hasChildren(m_db);
	bool isPrepare = false;
	
	foreach (const QVariant v,idNodeList)
	{
		quint64 idNode = v.toULongLong();

		if (m_recordMap.contains(idNode))
		{
			outList << m_recordMap[idNode];
			continue;
		}
			
		if (!isPrepare)
		{
			selNode.prepare(fieldList,VSqlFieldList() << node.idNode());
			isPrepare = true;

			if (VSqlLog::canWrite())
				VSqlLog::write("VSqlNodeMap::recordPtrList_byIdNodeList",selNode.sql());
		}

		node.setIdNode(idNode);
		selNode.exec(VSqlFieldList() << node.idNode());

		if (!selNode.next(fieldPtrList))
			continue;

		VSqlSharedRecordPtr newRecord(new QVariantList);
			node.setRecord(newRecord);
			
		m_recordMap[idNode] = newRecord;

		VSqlNodeChildren nodeChildren;
			nodeChildren.setHasChildren(hasChildren.exec(idNode));
		m_childrenMap[idNode] = nodeChildren;

		outList << newRecord;
	}
	
	return outList;
}

//
VSqlSharedRecordPtr VSqlNodeMap::recordPtr_byIdNode(const QVariant &idNode)
{
	QList<VSqlSharedRecordPtr> list = recordPtrList_byIdNodeList(QVariantList() << idNode);
	if (list.count())
		return list[0];

	return VSqlSharedRecordPtr();
}

//
QList<VSqlNode> VSqlNodeMap::nodeList_byIdNodeList(const QVariantList &idNodeList)
{
	QList<VSqlNode> outList;
	
	foreach (const VSqlSharedRecordPtr record,recordPtrList_byIdNodeList(idNodeList))
	{
		VSqlNode node;
			node.setValues(record);
		outList << node;
	}
	
	return outList;
}

//
VSqlNode VSqlNodeMap::node_byIdNode(const QVariant &idNode)
{
	VSqlNode node;
	VSqlSharedRecordPtr record = recordPtr_byIdNode(idNode);
	if (record)
		node.setValues(record);

	return node;
}

//
VSqlNodeChildren VSqlNodeMap::children_byIdNode(const QVariant &idNode)
{
	quint64 vIdNode = idNode.toULongLong();

	if (!m_recordMap.contains(vIdNode))
		m_recordMap[vIdNode] = recordPtr_byIdNode(idNode);

	VSqlNodeChildren children = m_childrenMap[vIdNode];
	if (children.childIdNodeList().count() == 0)
	{
		children = selectChildren_byIdNode(idNode);
		m_childrenMap[vIdNode] = children;
	}

	return children;
}

//
QList<VSqlSharedRecordPtr> VSqlNodeMap::childrenRecordPtrList_byIdNode(const QVariant &idNode)
{
	return recordPtrList_byIdNodeList(
		children_byIdNode(idNode).childIdNodeList()
	);
}

//
QList<VSqlNode> VSqlNodeMap::childrenNodeList_byIdNode(const QVariant &idNode)
{
	return nodeList_byIdNodeList(
		children_byIdNode(idNode).childIdNodeList()
	);
}

//
QList<VSqlSharedRecordPtr> VSqlNodeMap::recordPtrList_byRefIdItem(
	const QVariant &refIdItem,
	const QVariant &idNodeParent
)
{
	QString sql =
		"select idNode "
		"from   VidNode_idNode_for_refIdItem(:IdNodeParent,:refIdItem) ";




	VSqlNode node;
	VSqlNode_select_byAnd selNode(m_db);

	selNode.prepare
	(
		VSqlFieldList() << node.idNode(),
		VSqlFieldList() << node.refIdItem()
	);

	if (VSqlLog::canWrite())
		VSqlLog::write("VSqlNodeMap::recordPtrList_byRefIdItem",selNode.sql());

	node.setRefIdItem(refIdItem);
	selNode.exec(VSqlFieldList() << node.refIdItem());

	QList<VSqlSharedRecordPtr> list;

	if (idNodeParent.isNull())
	{
		while (selNode.next(VSqlFieldPtrList() << node.idNodePtr()))
			list << recordPtr_byIdNode(node.idNode().value());
	}
	else
	{
		QString sql =
			"select * "
			"from VidNode_parents_for_idNode(:idNode) p "
			"where p.idNodeParent = :idNodeParent";

		if (VSqlLog::canWrite())
			VSqlLog::write("VSqlNodeMap::recordPtrList_byRefIdItem",sql);

		VSqlQuery selParents(m_db);
		bool isOk = selParents.prepare(sql);
		Q_ASSERT(isOk == true);
		
		while (selNode.next(VSqlFieldPtrList() << node.idNodePtr()))
		{
			selParents.bindValue("idNode",node.idNode().value());
			selParents.bindValue("idNodeParent",idNodeParent);
			selParents.exec();
			
			if (selParents.next())
				list << recordPtr_byIdNode(node.idNode().value());
		}
	}
	
	return list;
}

//
QList<VSqlNode> VSqlNodeMap::nodeList_byRefIdItem(const QVariant &refIdItem,const QVariant &idNodeParent)
{
	QList<VSqlNode> list;
	
	foreach (VSqlSharedRecordPtr record,recordPtrList_byRefIdItem(refIdItem,idNodeParent))
	{
		VSqlNode node;
			node.setValues(record);
		list << node;
	}
	
	return list;
}

//
bool VSqlNodeMap::updateValue(const QVariant &idNode,int column,const QVariant &value)
{
	VSqlSharedRecordPtr record = recordPtr_byIdNode(idNode);
	if (!record)
		return false;

	(*record)[column] = value;

	VSqlNode node;
		node.setValues(record);
	QSqlField fieldUpdate = node.fieldList()[column];
	QSqlField fieldIdNode = node.idNode();

	QSqlDatabase db(m_db);
	db.transaction();
	VSqlQuery_update update(db);

	update.prepare
	(
		node.tableName(),
		VSqlFieldList() << fieldUpdate,
		QString() = fieldIdNode.name() + " = :" + fieldIdNode.name()
	);

	update.exec
	(
		VSqlFieldList() << fieldUpdate,
		VSqlFieldList() << fieldIdNode
	);

	db.commit();
	return true;
}

//
VSqlNodeChildren VSqlNodeMap::selectChildren_byIdNode(const QVariant &idNode)
{
	QVariantList childIdNodeList;
	VSqlNode node;
	VSqlFieldList fieldList = node.fieldList();
	VSqlFieldPtrList fieldPtrList = node.fieldPtrList();
	VSqlNode_select_byAnd selNode(m_db);

	selNode.prepare
	(
		fieldList,
		VSqlFieldList() << node.idNodeParent()
	);

	if (VSqlLog::canWrite())
		VSqlLog::write("VSqlNodeMap::selectChildren_byIdNode",selNode.sql());

	node.setIdNodeParent(idNode);
	selNode.exec(VSqlFieldList() << node.idNodeParent());

	while (selNode.next(fieldPtrList))
	{
		QVariant childIdNode = node.idNode().value();
		quint64 vChildIdNode = childIdNode.toULongLong();
	
		if (!m_recordMap.contains(vChildIdNode))
		{
			VSqlSharedRecordPtr newRecord(new QVariantList);
				node.setRecord(newRecord);
			m_recordMap[vChildIdNode] = newRecord;
		}
		
		childIdNodeList << childIdNode;
	}

	VSqlNodeChildren children;
		children.setChildIdNodeList(childIdNodeList);
		
	return children;
}

//
void VSqlNodeMap::insertChildren(const QVariant &idNode,const QList<VSqlNode*> &nodeChildListPtr)
{
	if (nodeChildListPtr.count() == 0)
		return;
		
	quint64 vIdNode = idNode.toULongLong();
	VSqlNodeChildren children = m_childrenMap[vIdNode];
	VSqlNode_insert insNode(m_db);

	if (VSqlLog::canWrite())
		VSqlLog::write("VSqlNodeMap::insertChildren",insNode.sql());

	foreach (VSqlNode *nodeChild,nodeChildListPtr)
	{
		nodeChild->setIdNodeParent(idNode);
		insNode.exec(*nodeChild);

		VSqlSharedRecordPtr newRecord(new QVariantList);
			nodeChild->setRecord(newRecord);

		quint64 vIdNodeChild = nodeChild->idNode().value().toULongLong();
		m_recordMap[vIdNodeChild] = newRecord;
		
		children.appendChildIdNode(vIdNodeChild);
	}

	m_childrenMap[vIdNode] = children;
}

//
void VSqlNodeMap::insertChild(const QVariant &idNode,VSqlNode *nodeChildPtr)
{
	insertChildren(idNode,QList<VSqlNode*>() << nodeChildPtr);
}
